#pragma once

class ICalc;
class Thread;

/*--------------------------------------------------------------------------*/

struct ECalc
{
	enum Type
	{
	    SingleThreadHighPriority = 9990,
	    SingleThreadLowPriority,
	    MultiThreadHighPriority,
	    MultithreadLowPriority
	};
};

/*--------------------------------------------------------------------------*/

struct CalcArg
{
	ICalc*			m_calc;
	uint	m_arg;

	CalcArg() :
		m_calc(NULL),
		m_arg(0)
	{}

	CalcArg(ICalc* calc, uint arg) :
		m_calc(calc),
		m_arg(arg)
	{}
};

/*--------------------------------------------------------------------------*/

struct TaskContainer
{
	TaskContainer() :
		m_singlethread_highpriority_count(0),
		m_multithread_highpriority_count(0)
	{}

	std::queue<CalcArg> m_singlethread_highpriority;
	uint m_singlethread_highpriority_count;

	std::queue<CalcArg> m_multithread_highpriority;
	uint m_multithread_highpriority_count;

	static std::queue<CalcArg> m_singlethread_lowpriority;
	static uint m_singlethread_lowpriority_count;

	static std::queue<CalcArg> m_multithread_lowpriority;
	static uint m_multithread_lowpriority_count;
};

/*--------------------------------------------------------------------------*/

class CCalc final :
	public IEngMod,
	public ICriticalSection
{
public:
	CCalc();
	virtual ~CCalc();

	/*-EngMod-------------------------------------------------------------------*/
	virtual void		OnLoop(uint i) override;

	/*--------------------------------------------------------------------------*/

	void				AddToNextFrame(ICalc* bc, uint flags, uint arg);
	void				AddToCurrent(ICalc* bc, uint flags, uint arg);
	void				StartThreads();
	void				EnableMultithreading();
	void				DoCalculations();
	void				GetNextTask(uint pid, CalcArg& task);

	/*--------------------------------------------------------------------------*/

	bool				AreHighPriorityTasksAvailable() const;
	bool				AreChildrenBusy() const;
	uint				GetTasksLeft() const;

	/*--------------------------------------------------------------------------*/

	void				AddNewThread();
	void				RemoveThread();
	void				ListThreads();

	/*--------------------------------------------------------------------------*/

	void				RegisterNewAsyncTask();
	void				UnregisterNewAsyncTask();
	bool				LimitAsyncTask();

private:
	void				FlipTaskContainers();
	void				GetTaskFromList(uint flags, CalcArg& task);

	TaskContainer*		m_taskContainer;
	TaskContainer*		m_futureTaskContainer;

	uint				m_calcQueueStartTime;
	uint				m_taskDoneSoFarByMainThread;

	ThreadPtnVec		m_threads;
	uint				m_threadsCount;
	Variable*			var_calc_frametime;

	uint				m_asyncTasks;

	std::vector<ICalc*>	m_ICalcInstances;
};

namespace sh
{
void listthreads();
};

extern CCalc* g_Calc;